package com.sdp.core.bizc.redis;


import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.sdp.core.bizc.exception.ApplicationException;
import com.sdp.core.bizc.listener.CacheListener;
import com.sdp.core.bizc.vo.CacheInfo;

@Component
public class RedisUtil {

	@Autowired
    private RedisTemplate<String, Object> redisTemplate;
	
	Lock lock = new ReentrantLock();//锁
	
	@Autowired
	private CacheListener cacheListener;
	

	/**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key){
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
        	e.printStackTrace();
        	return false;
        } 
    }
    
    public boolean hasKey(Class<?> clazz){
    	return this.hasKey(clazz.getName());
    }
    
    /**
              * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
	public void del(String ... key){
        if(key!=null&&key.length>0){
            if(key.length==1){
                redisTemplate.delete(key[0]);
            }else{
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }
    
    public void del(Class<?> clazz){
    	this.del(clazz.getName());
    }
    
    public void del(Class<?> clazz, String... filed){
    	String fieldKey = null;
		if(filed != null && filed.length > 0) {
			fieldKey = getFieldKey(filed);
		}
    	this.delMap(clazz.getName(), fieldKey);
    }
    
  //============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key){
        return key==null?null:redisTemplate.opsForValue().get(key);
    }
    
    /**
     * 一次取多个key，适用于少量的 key
     * @param keys
     * @return
     */
    public List<Object> get(List<String> keys){
        return keys==null || keys.size()==0?null:redisTemplate.opsForValue().multiGet(keys);
    }
    
    /**
     * 一次根据多个KEY取值，适用于大量KEY
     * @param keys
     * @return
     */
    public List<Object> getUsingPipeline(List<String> keys) {
        return redisTemplate.executePipelined(new SessionCallback<Object>() {
        	@Override
            public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
                for (String key : keys) {
                    redisOperations.opsForValue().get(key);
                }
                return null;
            }
        });
    	
    }
    
    @SuppressWarnings("unchecked")
	public <T> T  get(Class<?> clazz) {
    	return (T) this.get(clazz.getName());
    }

    /**
             * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key,Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public boolean set(String key,Object value, long time) {
    	try {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public boolean set(Class<?> clazz,Object value) {
    	return this.set(clazz.getName(), value);
    }
    
  //================================Map=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param filed 项 不能为null
     * @return 值
     */
    public Object getMap(String key,String filed){
        return redisTemplate.opsForHash().get(key, filed);
    }
    
    @SuppressWarnings("unchecked")
	public <T> T getMap(Class<T> clazz,String filed) {
    	return (T) redisTemplate.opsForHash().get(clazz.getName(), filed);
    }
    
    public List<Object> getMap(String key,Collection<Object> fields){
    	return redisTemplate.opsForHash().multiGet(key, fields);
    }
    
    @SuppressWarnings("unchecked")
	public <T>List<T> getMap(Class<T> clazz,Collection<Object> fields){
    	return (List<T>) redisTemplate.opsForHash().multiGet(clazz.getName(), fields);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object,Object> getMap(String key){
        return redisTemplate.opsForHash().entries(key);
    }
    
    @SuppressWarnings("unchecked")
	public <T> Map<Object,T> getMap(Class<T> clazz){
    	return (Map<Object, T>) redisTemplate.opsForHash().entries(clazz.getName());
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean setMap(String key, Map<? extends Object,Object> map){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public boolean setStringMap(String key, Map<String,String> map){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public <T> boolean setEntityMap(String key, Map<String,T> map){
    	try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    } 
    
    public <T> boolean setMap(Class<?> clazz, Map<String,T> map){
    	try {
    		redisTemplate.opsForHash().putAll(clazz.getName(), map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    } 
    
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param filed 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean setMap(String key,String filed,Object value) {
        try {
            redisTemplate.opsForHash().put(key, filed, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public boolean setMap(Class<?> clazz,String filed,Object value) {
    	return this.setMap(clazz.getName(), filed, value);
    }
    
    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void delMap(String key, Object... filed){
        redisTemplate.opsForHash().delete(key,filed);
    }
    
    public void delMap(Class<?> clazz, Object... filed){
    	this.delMap(clazz.getName(), filed);
    }
    
    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param filed 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean mapHasKey(String key, String filed){
        return redisTemplate.opsForHash().hasKey(key, filed);
    }
    
    public boolean mapHasKey(Class<?> clazz, String filed){
    	return this.mapHasKey(clazz.getName(), filed);
    }
    
    
    //===============================list=================================

    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束  0 到 -1代表所有值
     * @return
     */
    public List<Object> getList(String key, long start, long end){
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long getListSize(String key){
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引  index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @return
     */
    public Object getListBYIndex(String key,long index){
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean setList(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean setList(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean updateListByIndex(String key, long index,Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    
    /**
     * 模糊查询获取key值
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern){
        return redisTemplate.keys(pattern);
    }
    
    /**
     * 获取map的Field值
     * @param arg
     * @return
     */
    public String getFieldKey(String... arg)
	{
		StringBuffer sbf = new StringBuffer();
		for (int i = 0, count = arg.length; i < count; i++)
		{
			if(i<arg.length-1) {
				sbf.append(arg[i]).append("#");
			}else {
				sbf.append(arg[i]);
			}
			
		}
		return sbf.toString();
	}
    
    
    public long getSerialNumber(String key){
    	return redisTemplate.opsForValue().increment(key);
   
    }
    
    /**
     * @Title: expire
     * @Description: 设置过期时间
     * @param key
     * @param time
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 递减计数器
     * @param key
     * @return
     */
    public long decrement(String key) {
    	return redisTemplate.opsForValue().decrement(key);
    }
    
    @SuppressWarnings("unchecked")
    public <T> T getCache(String cacheKey,String... arg) {
    	Object cacheObj = null;
    	try {
			lock.lock();//锁住锁
			String fieldKey = null;
			if(arg != null && arg.length > 0) {
				fieldKey = getFieldKey(arg);
			}
			if(hasKey(cacheKey)) {
				if(StringUtils.isEmpty(fieldKey)) {
					cacheObj = get(cacheKey);
				}else {
					cacheObj = getMap(cacheKey, fieldKey);
				}
			}
			
			if(cacheObj!=null) {
				return (T) cacheObj;
			}
			
			CacheInfo cacheInfo = cacheListener.getCacheMap().get(cacheKey);
			if(cacheInfo==null) {
				return (T) cacheObj;
			}
			
			Type[] types = cacheInfo.getMethod().getGenericParameterTypes();
			if(types.length==0) {
				cacheObj = cacheInfo.getMethod().invoke(cacheInfo.getBean());
			}else {
				
				Object[] argObj = new Object[types.length];
				for (int i = 0, count = types.length; i < count; i++){
					argObj[i] = arg[i];
				}
				cacheObj =  cacheInfo.getMethod().invoke(cacheInfo.getBean(),argObj);
			}
			
			if(StringUtils.isEmpty(fieldKey)) {
				set(cacheKey, cacheObj);
			}else {
				setMap(cacheKey, fieldKey, cacheObj);
			}
			return (T) cacheObj;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ApplicationException(e.getMessage());
		}finally {
			lock.unlock();//释放锁
		}
    }
}
